第6章(2) 软件测试
第6章(2) 软件测试
软件测试基础
软件测试的目标:
- 测试是为了发现程序中的错误而执行程序的过程(定义)
- 好的测试方案是极可能发现迄今为止尚未发现的错误的测试方案
- 成功的测试是发现了至今为止尚未发现的错误的测试
软件测试的准则:
- 所有的测试都应该追溯到用户需求
- 应该远在测试开始之前就指定出测试计划
- 把Pareto原理应用到软件测试中
- 应该从“小规模”测试开始,并逐步进行“大规模”测试
- 穷举测试是不可能的
- 为了达到最佳的测试效果,应该由独立的第三方从事测试工作
测试方法:
- 黑盒测试:如果已经知道了产品应该具有的功能,可以通过测试来检验是否每个功能都能正常使用
- 白盒测试:如果知道产品的内部工作过程,可以通过测试来检验产品内部动作是否按照规格说明书的规定正常进行
黑盒测试(功能测试):
把程序看作一个黑盒子;
完全不考虑程序的内部结构和处理过程;
是在程序接口进行的测试。
白盒测试(结构测试):
把程序看成装在一个透明的盒子里;
测试者完全知道程序的结构和处理算法;
按照程序内部的逻辑测试程序,检测程序中的主要执行通路是否都能按预定要求正确工作。
黑盒测试与白盒测试优缺点比较:
测试步骤:
模块测试(单元测试)
目的:保证每个模块作为一个单元能正确运行
这个测试步骤所发现的往往是编码和详细设计的错误
模块的编写者和测试者是同一个人
子系统测试
定义:把经过单元测试的模块放在一起形成一个子系统来测试
着重测试模块的接口,模块相互间的协调和通信是这个测试过程中的主要问题
系统测试
把经过测试的子系统装配成一个完整的系统来测试
发现的往往是软件设计中的错误,也可能发现需求说明中的错误;
不论是子系统测试还是系统测试,都兼有检测和组装两重含义,通常称为集成测试。
验收测试(确认测试)
把软件系统作为单一的实体进行测试;
它是在用户积极参与下进行的,而且可能主要使用实际数据(系统将来要处理的信息)进行测试;
发现的往往是系统需求说明书中的错误。
平行运行
同时运行新开发出来的系统和将被它取代的旧系统,比较新旧两个系统的处理结果;
目的:
- 可以在准生产环境中运行新系统而又不冒风险;
- 用户能有一段熟悉新系统的时间;
- 可以验证用户指南和使用手册之类的文档;
- 能够以准生产模式对新系统进行全负荷测试,可以用测试结果验证性能指标。
软件测试阶段的信息流:
输入信息有两类:
软件配置:包括需求说明书、设计说明书和源程序清单等;
测试配置:包括测试计划和测试方案。
单元测试
单元测试总述:
- 单元测试集中检测软件设计的最小单元——模块;
- 单元测试和编码属于软件过程的同一个阶段;
- 可以应用人工测试和计算机测试这样两种不同类型的测试方法;
- 单元测试主要使用白盒测试技术,对多个模块的测试可以并行地进行。
测试重点:
模块接口
- 参数的数目、次序、属性或单位系统与变元是否一致
- 是否修改了只作输入用的变元
- 全局变量的定义和用法在各个模块中是否一致
局部数据结构:对于模块来说,局部数据结构是常见的错误来源
重要的执行通路
出错处理通路
边界条件
代码审查
由审查小组正式进行测试称为代码审查;
对于典型的 程序来说,可以查出30%~70%的逻辑设计错误和编码错误;
审查小组组成:
- 组长
- 程序的设计者
- 程序的编写者
- 程序的测试者
程序的设计者、编写者和测试者通常不是同一个人
审查小组的任务是发现错误而不是改正错误
代码审查比计算机测试优越的是:一次审查会上可以发现许多错误,可以减少系统验证的总工作量。
计算机测试
模块并不是一个独立的程序,因此必须为每个单元测试开发驱动程序和(或)存根程序。
驱动程序通常是一个“主程序”,它接收测试数据,传送给被测试的模块,并且印出有关的结果。
存根程序代替被测试的模块所调用的模块。它使用被它代替的模块的接口,可能做最少量的数据操作,印出对入口的检验或操作结果,并且把控制归还给调用它的模块。
驱动程序和存根程序代表开销,通常并不把它们作为软件产品的一部分交给用户。
模块的内聚程度高可以简化单元测试的过程。
集成测试
集成测试是测试和组装软件的系统化技术,主要目标是发现与接口有关的问题。
由模块组装成程序时有两种方法:
非渐增式测试方法:先分别测试每个模块,再把所有模块按设计要求放在一起结合成所要的程序
缺点:
- 非渐增式测试一下子把所有模块放在一起,并把庞大的程序作为一个整体来测试,测试者面对的情况十分复杂。
- 在庞大的程序中想要诊断定位一个错误是非常困难的,改正错误更是极端困难,而且一旦改正一个错误之后,马上又会遇到新的错误。
渐增式测试方法:把下一个要测试的模块同已经测试好的那些模块结合起来进行测试,测试完以后再把下一个应该测试的模块结合进来测试,每次增加一个模块,实际上同时完成单元测试和集成测试。
优点:
- 把程序划分成小段来构造和测试,在这个过程中比较容易定位和改正错误;
- 对接口可以进行更彻底的测试
- 可以使用系统化的测试方法
渐增方式有两种集成策略:
- 自顶向下
- 自底向上
自顶向下集成
从主控制模块开始,沿着程序的控制层次向下移动,逐渐把各个模块结合起来。在把附属于主控制模块的那些模块组装到程序结构中去时,或者使用深度优先的策略,或者使用宽度优先的策略。
深度优先:先组装在软件结构的一条主控制通路上的所有模块。
宽度优先:沿软件结构水平地移动,把处于同一个控制层次上的所有模块组装起来。
把模块结合进软件结构的过程由下述4个步骤完成:
- 第一步,对主控制模块进行测试,测试时用存根程序代替所有直接附属于主控制模块的模块;
- 第二步,根据选定的结合策略(深度优先或宽度优先),每次用一个实际模块代换一个存根程序(新结合进来的模块往往又需要新的存根程序);
- 第三步,在结合进一个模块的同时进行测试;
- 第四步,为了保证加入模块没有引进新的错误,可能需要进行回归测试(即,全部或部分地重复以前做过的测试)。
- 从第二步开始不断地重复进行上述过程,直到构造起完整的软件结构为止。
优点:
- 能够在测试的早期对主要的控制或关键的抉择进行检验。
- 如果选择深度优先的结合方法,可以在早期实现软件的一个完整的功能并且验证这个功能。
**缺点:**存根程序代替了低层次的模块,在软件结构中没有重要的数据自下往上流。
自底向上集成
自底向上测试属于“原子”模块(即在软件结构最低层的模块)开始组装和测试。因为是从底部向上结合模块,总能得到所需的下层模块处理功能,所以不需要存根程序。
步骤:
- 第一步,把低层模块组合成实现某个特定的软件子功能的族;
- 第二步,写一个驱动程序(用于测试的控制程序),协调测试数据的输入和输出;
- 第三步,对由模块组成的子功能族进行测试;
- 第四步,去掉驱动程序,沿软件结构自下向上移动,把子功能族组合起来形成更大的子功能族。
- 上述第二步到第四步实质上构成了一个循环。
混合策略:
**改进的自顶向下测试方法:**基本上使用自顶向下的测试方法,但是在早期使用自底向上的方法测试软件中的少数关键模块。
优点:能在测试的早期发现关键模块中的错误;
缺点:测试关键模块时需要驱动程序。
混合法:对软件结构中较上层使用的自顶向下方法与对软件结构中较下层使用的自底向上方法相结合。
兼有两种方法的优缺点,当被测试的软件中关键模块比较多时,这种混合法可能是最好的折衷方法。
回归测试
回归测试是指重新执行已经做过的测试的某个子集,以保证测试过程中的变化没有带来非预期的副作用。
回归测试就是用于保证由于调试或其他原因引起的变化,不会导致非预期的软件行为或额外错误的测试活动。
回归测试可以通过重新执行全部测试用例的一个子集人工地进行,也可以使用自动化的捕获回放工具自动进行。
回归测试集(已执行锅的测试用例的子集)包括下述三类不同的测试:
- 检测软件全部功能的代表性测试用例
- 专门针对可能受修改影响的软件功能和附加测试
- 针对被修改过的软件成分的测试
回归测试集应该设计成只包括可以检测程序每个主要功能中的一类或多类错误的那样一些测试用例。
确认测试
确认测试也称为验收测试,它的目标是验证软件的有效性。
软件有效性的一个简单定义是:如果软件的功能和性能如同用户所合理期待的那样,软件就是有效的。
需求分析阶段产生的软件需求规格说明书,准确地描述了用户对软件的合理期望,因此是软件有效性的标准,也是进行确认测试的基础。
确认测试的范围
确认测试必须有用户积极参与,或者以用户为主进行。
使用用户界面输入测试数据并且分析评价测试的输出结果。
通常在验收之前由开发单位对用户进行培训。
确认测试通常使用黑盒测试法。
软件配置复查
确认测试的一个重要内容是复查软件配置。
复查的目的是保证软件配置的所有成分都齐全,质量符合要求,文档与程序完全一致,具有完成软件维护所必须的细节,而且已经编好目录。
Alpha和Beta测试
Alpha测试由用户在开发者的场所进行,并且在开发者对用户的“指导”下进行测试。Alpha测试是在受控的环境中进行的。
Beta测试由软件的最终用户们在一个或多个客户场所进行。开发者通常不在Beta测试的现场,因此,Beta测试是软件在开发者不能控制的环境中的“真实”应用。
接口测试
每个接口可能有多个输入参数,每个参数有 “典型值”、“边界值”、“异常值”之分。
根据接口的定义,可以推断某种输入应当产生什么样的输出。输出包括函数的返回值和输出参数。
观察是否有程序语句从来没有被执行过。
要特别留意函数体内的错误处理程序块。
路径测试
想遍历测试几乎是不可能的,不测试或者胡乱找几条路径测试却又不行。
输入与对应的输出之间的路径是唯一的。由于接口测试时的输入是有代表性的,因此相应的路径也具有代表性。
路径测试检查表:数据类型、变量值、逻辑判断、循环、内存管理、文件I/O、错误处理。
功能测试
功能测试的基本方法是构造一些合理输入(在需求范围之内),检查输出是否与期望相同。
功能测试有两种比较好的测试方法:等价划分法和边界值分析法。
等价划分是指把输入空间划分为几个“等价区间”,在每个“等价区间”中只需要测试一个典型值就可以了。
边界值测试法是对等价划分法的补充。除了典型值外还要用边界值作为测试用例。
健壮性测试
健壮性是指在异常情况下,软件能正常运行的能力。
健壮性有两层含义:
- 容错能力。容错性测试通常构造一些不合理的输入来引诱软件出错。
- 恢复能力 。恢复测试重点考察以下几项:
- 系统能否重新运行;
- 有无重要的数据丢失;
- 是否毁坏了其它相关的软件硬件。
性能测试
性能测试即测试软件处理事务的速度,一是为了检验性能是否符合需求,二是为了得到某些性能数据供人们参考。
有时人们关心测试的“绝对值” 。
有时人们关心测试的“相对值” 。
用户界面测试
绝大多数软件拥有图形用户界面。
图形用户界面的测试重点是正确性、易用性和视觉效果。
在评价易用性和视觉效果时,主观性非常强,应当考虑多个人的观点。
信息安全测试
信息安全性是指防止系统被非法入侵的能力,既属于技术问题又属于管理问题。
信息安全性测试有如下步骤:
- 为非法入侵设立目标;
- 邀请(或悬赏)一些人扮演黑客,让他们想尽办法入侵系统,实现“目标”。
- 如果有人成功了,请他详述入侵的过程。
压力测试
压力测试也叫负荷测试,即获取系统能正常运行的极限状态。
压力测试的主要任务是:构造正确的输入,使劲折腾系统却让它刚好不瘫痪。
压力测试的一个变种是敏感测试。
敏感测试目的是发现什么样的输入可能会引发不稳定现象。
可靠性测试
可靠性是指在一定的环境下、给定的时间内、系统不发生故障的概率。
软件可靠性测试可能会花费很长时间。
比较实用的办法是,让用户使用该系统,记录每一次发生故障的时刻。计算出相邻故障的时间间隔,注意要去掉非工作时间。
统计出不发生故障的“最小时间间隔”、“最大时间间隔”和“平均时间间隔”。
安装/反安装测试
目前市面上有非常流行的、专门制作安装/反安装程序的一些工具,如Install Shelled。
主要测试工作:
- 至少在标准配置和最低配置两种环境下测试;
- 如果有安装界面,应当尝试各种选项,如选择“全部”、“部分”、“升级”等。
白盒测试技术
设计测试方案是测试阶段的关键技术问题。所谓测试方案包括具体的测试目的(例如,预定要测试的具体功能),应该输入的测试数据和预期的结果。通常又把测试数据和预期的输出结果称为测试用例。
设计测试方案的基本目标是:确定一组最可能发现某个错误或某类错误的测试数据。
逻辑覆盖
有选择地执行程序中某些最有代表性的通路是对穷尽测试的惟一可行的替代办法。
逻辑覆盖是对一系列测试过程的总称,这组测试过程逐渐进行越来越完善的通路测试。
测试数据执行(或叫覆盖)程序逻辑的程度,从覆盖源程序语句的详尽程度分析,大致有以下一些不同的覆盖标准:
语句覆盖
含义:选择足够多的测试数据,使被测程序中每个语句至少执行一次。
**特点:**语句覆盖对程序的逻辑覆盖很少。语句覆盖只关心判定表达式的值,而没有分别测试判定表达式中每个条件取不同值时的情况。语句覆盖是很弱的逻辑覆盖标准。
判定覆盖
**含义:**不仅每个语句必须至少执行一次,而且每个判定的每种可能的结果都应该至少执行一次。
条件覆盖
**含义:**不仅每个语句至少执行一次,而且使判定表达式中的每个条件都取到各种可能的结果。
**特点:**条件覆盖通常比判定覆盖强,因为它使每个条件都取到了两个不同的结果,判定覆盖却只关心整个判定表达式的值。但也有反例,如第二组测试用例。
判定覆盖不一定包含条件覆盖,条件覆盖也不一定包含判定覆盖。
判定/条件覆盖
**含义:**使得判定表达式中的每个条件都取到各种可能的值,每个判定表达式也都取到各种可能的结果。
条件组合覆盖
**含义:**要求选取足够多的测试数据,使得每个判定表达式中条件的各种可能组合都至少出现一次。
**特点:**条件组合覆盖是前述几种覆盖标准中最强的。满足条件组合覆盖标准的测试数据,也一定满足判定覆盖、条件覆盖和判定/条件覆盖标准。
但是,条件组合覆盖标准的测试数据并不一定能使程序中的每条路径都执行到。
从对程序路径的覆盖程度分析的逻辑覆盖标准:
点覆盖
**含义:**选取足够多的测试数据,使得程序执行路径至少经过流图的每个结点一次。
**特点:**由于流图的每个结点与一条或多条语句相对应,因此点覆盖标准和语句覆盖标准是相同的。
边覆盖
**含义:**选取足够多测试数据,使得程序执行路径至少经过流图中每条边一次。
**特点:**通常边覆盖和判定覆盖是一致的。
路径覆盖
**含义:**选取足够多测试数据,使程序的每条可能路径都至少执行一次(如果程序图中有环,则要求每个环至少经过一次)。
控制结构测试
基本路径测试
基本路径测试是Tom McCabe提出的一种白盒测试技术。
首先计算程序的环形复杂度;
以该复杂度为指南定义执行路径的基本集合;
从该基本集合导出的测试用例可保证程序中的每条语句至少执行一次,而且每个条件在执行时都将分别取真、假两种值。
条件测试 着重测试程序中的每个条件。
优点:
- 容易度量条件的测试覆盖率
- 程序内条件的测试覆盖率可指导附加测试的设计
目的:不仅是检测程序条件中的错误,而且是检测程序中的其他错误
循环测试
循环测试是一种白盒测试技术,它专注于测试循环结构的有效性。
在结构化的程序中通常只有3种循环,即简单循环、串接循环和嵌套循环。
简单循环
应该使用下列测试集来测试简单循环,其中n是允许通过循环的最大次数。
- 跳过循环。
- 只通过循环一次。
- 通过循环两次。
- 通过循环m次,其中m<n-1。
- 通过循环n-1,n,n+1次。
嵌套循环
从最内层循环开始测试,其他循环都设置为最小值。
对最内层循环使用简单循环测试方法,而使外层循环的迭代参数取最小值,并为越界值或非法值增加一些额外的测试。
由内向外,对下一个循环进行测试,但保持所有其他外层循环为最小值,其他嵌套循环为“典型”值。
继续进行下去,直到测试完所有循环。
串接循环
如果串接循环的各个循环都彼此独立,则可以使用测试简单循环的方法来测试串接循环。
如果两个循环串接,而且第一个循环的循环计数器值是第二个循环的初始值,则这两个循环并不是独立的。当循环不独立时,建议使用测试嵌套循环的方法来测试串接循环。
黑盒测试技术
黑盒测试着重测试软件功能,错误类型为:
- 功能不正确或遗漏了功能;
- 界面错误;
- 数据结构错误或外部数据库访问错误;
- 性能错误;
- 初始化和终止错误。
测试标准:
- 测试用例尽可能少;
- 一个测试用例能指出一类错误。
等价划分
等价划分是一种黑盒测试技术,把程序的输入域划分成若干个数据类,据此导出测试用例。
等价划分法力图设计出能发现若干类错误的测试用例,从而减少测试用例的数目。
每类中的一个典型值在测试中的作用与这一类中所有其他值的作用相同。
使用等价划分法设计测试方案首先需要划分输入数据的等价类。
常常还需要分析输出数据的等价类,以便根据输出数据的等价类导出对应的输入数据等价类。
等价类划分的启发式规则:
- 如果规定了输入值的范围,则可划分出一个有效的等价类(输入值在此范围内),两个无效的等价类(输入值小于最小值或大于最大值);
- 如果规定了输入数据的个数,则类似地也可划分出一个有效的等价类和两个无效的等价类;
- 如果规定了输入数据的一组值,而且程序对不同输入值做不同处理,则每个允许的输入值是一个有效的等价类,此外还有一个无效的等价类(任一个不允许的输入值);
- 如果规定了输入数据必须遵循的规则,则可以划分出一个有效的等价类(符合规则)和若干个无效的等价类(从各种不同角度违反规则);
- 如果规定了输入数据为整型,则可以划分出正整数、零和负整数等3个有效类;
- 如果程序的处理对象是表格,则应该使用空表,以及含一项或多项的表。
设计测试方案时两个步骤:
- 设计一个新的测试方案以尽可能多地覆盖尚未被覆盖的有效等价类,重复这一步骤直到所有有效等价类都被覆盖为止;
- 设计一个新的测试方案,使它覆盖一个而且只覆盖一个尚未被覆盖的无效等价类,重复这一步骤直到所有无效等价类都被覆盖为止。 (通常程序发现一类错误后就不再检测是否有其他错误)
边界值分析
经验表明,处理边界情况时程序最容易发生错误。例如,许多程序错误出现在下标、纯量、数据结构和循环等等的边界附近。
使用边界值分析方法设计测试方案首先应该确定边界情况。选取的测试数据应该刚好等于、刚刚小于和刚刚大于边界值。
通常设计测试方案时总是联合使用等价划分和边界值分析两种技术。
错误推测
不同类型不同特点的程序通常又有一些特殊的容易出错的情况。因此必须依靠测试人员的经验和直觉,从各种可能的测试方案中选出一些最可能引起程序出错的方案。
错误推测法在很大程度上靠直觉和经验进行。它的基本想法是列举出程序中可能有的错误和容易发生错误的特殊情况,并且根据它们选择测试方案。
调试
调试(也称为纠错)作为成功测试的后果出现。
调试是在测试发现错误之后排除错误的过程。
软件错误的外部表现和它的内在原因之间可能并没有明显的联系。
调试就是把症状和原因联系起来的尚未被人深入认识的智力过程。
调试过程
调试发生在测试之后。
调试过程从执行一个测试用例开始,评估测试结果,如果发现实际结果与预期结果不一致,则这种不一致就是一个症状,它表明在软件中存在着隐藏的问题。
调试过程试图找出产生症状的原因,以便改正错误。
调试途径
蛮干法
蛮干法可能是寻找软件错误原因的最低效的方法。其他方法都失败时才使用这种方法。
这种方法印出内存的内容,激活对运行过程的跟踪,在程序中到处都写上WRITE(输出)语句。
更多情况下只会浪费时间和精力。必须首先进行周密的思考,有明确的目的,尽量减少无关信息的数量。
回溯法
回溯是一种相当常用的调试方法,当调试小程序时这种方法是有效的。
具体做法是,从发现症状的地方开始,人工沿程序的控制流往回追踪分析源程序代码,直到找出错误原因为止。
随着程序规模扩大,应该回溯的路径数目也变得越来越大,以至彻底回溯变成完全不可能了。
原因排除法
对分查找法
如果已经知道每个变量在程序内若干个关键点的正确值,则可以用赋值语句或输入语句在程序中点附近“注入”这些变量的正确值,然后运行程序并检查所得到的输出。
如果输出结果是正确的,则错误原因在程序前半部分;反之,错误原因在程序后半部分。
对错误原因所在的那部分重复使用这个方法,直到把出错范围缩小到容易诊断的程度为止。
归纳法
是从个别现象推断出一般性结论的思维方法。
首先把和错误有关的数据组织起来进行分析,以便发现可能的错误原因。
然后导出对错误原因的一个或多个假设,并利用已有的数据来证明或排除这些假设。
演绎法
从一般原理或前提出发,经过排除和精化的过程推导出结论。
首先设想出所有可能的出错原因,然后试图用测试来排除每一个假设的原因。
软件可靠性
测试阶段的根本目的是消除错误,保证软件的可靠性。
基本概念
**软件可靠性:**程序在给定的时间间隔内,按照规格说明书的规定成功地运行的概率。
**软件的可用性:**程序在给定的时间点,按照规格说明书的规定,成功地运行的概率。
**稳态可用性Ass:**如果在一段时间内,软件系统故障停机时间分别为td1,td2…,正常运行时间分别为tu1,tu2,…,则系统的稳态可用性为:
Ass=Tup/(Tup+Tdown)
其中Tup=∑tui,Tdown=∑tdi
**平均维修时间MTTR:**是修复一个故障平均需要用的时间,它取决于维护人员的技术水平和对系统的熟悉程度,也和系统的可维护性有重要关系。
**平均无故障时间MTTF:**是系统按规格说明书规定成功地运行的平均时间,它主要取决于系统中潜伏的错误的数目,因此和测试的关系十分密切。
Ass=MTTF/(MTTF+MTTR)
估算平均无故障时间的方法
1. 相关量的符号
- ET——测试之前程序中错误总数;
- IT——程序长度(机器指令总数);
- τ——测试(包括调试)时间;
- Ed(τ)——在0至τ期间发现的错误数;
- Ec(τ)——在0至τ期间改正的错误数。
2. 基本假定
在类似的程序中,单位长度里的错误数ET/IT近似为常数。通常:0.5×10-2≤ ET/IT ≤2×10-2
失效率正比于软件中剩余的错误数,而平均无故障时间MTTF与剩余的错误数成反比。
假设发现的每一个错误都立即正确地改正了。因此:Ec(τ)=Ed(τ)
剩余的错误数为:Er(τ)=ET-Ec(τ)
单位长度程序中剩余的错误数为:εr(τ)=ET/IT-Ec(τ)/IT
3. 估算平均无故障时间
经验表明,平均无故障时间与单位长度程序中剩余的错误数成反比,即:MTTF=1/K(ET/IT-Ec(τ)/IT),其中K为常数,典型值是200。
估算平均无故障时间的公式,可以评价软件测试的进展情况。Ec=ET-IT/(K×MTTF)
因此,也可以根据对软件平均无故障时间的要求,估计需要改正多少个错误之后,测试工作才能结束。
4. 估计错误总数的方法
程序中的错误总数ET与程序规模、类型、开发环境、开发方法论、开发人员的技术水平和管理水平等都有密切关系。
估计ET的两个方法:
**1. 植入错误法:**在测试之前由专人在程序中随机地植入一些错误。测试之后,根据测试小组发现的错误中原有的和植入的两种错误的比例,来估计程序中原有错误的总数ET 。
假设人为地植入的错误数为Ns,经过一段时间的测试之后发现ns个植入的错误,此外还发现了n个原有的错误。
如果可以认为测试方案发现植入错误和发现原有错误的能力相同,则能够估计出程序中原有错误的总数为:N=n/ns×Ns
**2. 分别测试法:**植入错误法的基本假定是所用的测试方案发现植入错误和发现原有错误的概率相同。
上述基本假定可能有时和事实不完全一致。
如果有办法随机地把程序中一部分原有的错误加上标记,然后根据测试过程中发现的有标记错误和无标记错误的比例,估计程序中的错误总数。
分别测试法使用两个测试员(小组),彼此独立地测试同一个程序的两个副本,把其中一个测试员发现的错误作为有标记的错误。
τ表示测试时间,设:
τ= 0 时错误总数为B0;
τ=τ1时测试员甲发现的错误数为B1;
τ=τ1时测试员乙发现的错误数为B2;
τ=τ1时两个测试员发现的相同错误数为bc。
如果认为测试员甲发现的错误是有标记的,即程序中有标记的错误总数为B1,则测试员乙发现的B2个错误中有bc个是有标记的。
假定测试员乙发现有标记错误和发现无标记错误的概率相同,则错误总数为:B0 =B2/bc×B1(标记总数除以发现标记的概率)